In [ ]:
from sklearn.feature_extraction.text import TfidfTransformer, CountVectorizer, TfidfVectorizer
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
from gensim.summarization import summarize


from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from nltk.corpus import stopwords
import numpy as np
import numpy.linalg as LA
from nltk.tokenize import sent_tokenize

import os
import nltk
import re
import sys
from nltk import NaiveBayesClassifier
import nltk.classify
from nltk.tokenize import wordpunct_tokenize
from nltk.corpus import stopwords
import re

from nltk.tokenize import RegexpTokenizer
from nltk.stem.snowball import RussianStemmer
from nltk.stem.snowball import SnowballStemmer
from gensim.summarization import keywords

import warnings
warnings.filterwarnings(action='ignore', category=UserWarning, module='gensim')

In [ ]:
df_ans = pd.read_csv("feedbacks_tags.csv", sep='\t')

In [ ]:
df_ans.head()

In [ ]:
mob_comment = pd.Series(df_ans['name'].tolist()).astype(str)
dist_train = mob_comment.apply(len)
dist_train = dist_train[dist_train>10]
 

pal = sns.color_palette()
plt.figure(figsize=(10, 5))
plt.hist(dist_train, bins=35, range=[0, 35], normed=True)
plt.title('Normalised histogram of character count in questions', fontsize=15)
plt.legend()
plt.xlabel('Number of characters', fontsize=15)
plt.ylabel('Probability', fontsize=15)

In [ ]:
dist_train = mob_comment.apply(lambda x: len(x.split(' ')))
dist_train = dist_train[dist_train>3]

plt.figure(figsize=(10, 5))
plt.hist(dist_train, bins=16, range=[1, 6], color=pal[2], normed=True, label='train')
plt.title('Normalised histogram of word count', fontsize=15)
plt.legend()
plt.xlabel('Number of words', fontsize=15)
plt.ylabel('Probability', fontsize=15)

In [ ]:
from wordcloud import WordCloud
cloud = WordCloud(width=1440, height=1080).generate(" ".join(mob_comment.astype(str)))
plt.figure(figsize=(15, 10))
plt.imshow(cloud)
plt.axis('off')

In [ ]:
stopWords = nltk.corpus.stopwords.words('russian')

In [ ]:
stopWords.append('все')
stopWords.append('очень')
stopWords.append('это')
stopWords.append('все')
stopWords.append('всем')

In [ ]:
mob_comment = df_ans[['count', 'name']]

In [ ]:
mob_comment.mobile_app_comment = mob_comment.name.str.lower()

In [ ]:
mob_comment.name.str.split(expand=True).stack().value_counts()[:10]

In [ ]:
tokenizer = nltk.word_tokenize

In [ ]:
mob_comment.mobile_app_comment = mob_comment.mobile_app_comment.str.replace(',', ' ')
mob_comment.mobile_app_comment = mob_comment.mobile_app_comment.str.replace('.', ' ')
mob_comment.mobile_app_comment = mob_comment.mobile_app_comment.str.replace('!', ' ')

In [ ]:
mob_comment['name'] = mob_comment['name'].apply(lambda x: ' '.join([word for word in x.split() if word not in (stopWords)]))

In [ ]:
mob_comment.name.str.split(expand=True).stack().value_counts()[:40]

In [ ]:
stemmer = SnowballStemmer("russian")

def tokenize_and_stem(text):
    # сначала токенизируем по предложению, потом по словам, чтобы сохранить "особые" слова
    tokens = [word for sent in nltk.sent_tokenize(text) for word in nltk.word_tokenize(sent)]
    filtered_tokens = []
    # удалим все самое плохое, знаки препинания
    for token in tokens:
        if re.search('[a-zA-Z0-9А-Яа-я]', token):
            filtered_tokens.append(token)
    stems = [stemmer.stem(t) for t in filtered_tokens]
    return stems

def tokenize_only(text):
    tokens = [word.lower() for sent in nltk.sent_tokenize(text) for word in nltk.word_tokenize(sent)]
    filtered_tokens = []
    for token in tokens:
        if re.search('[a-zA-Z0-9А-Яа-я]', token):
            filtered_tokens.append(token)
    return filtered_tokens

In [ ]:
totalvocab_stemmed = []
totalvocab_tokenized = []
for i in mob_comment.mobile_app_comment:
    allwords_stemmed = tokenize_and_stem(i) 
    totalvocab_stemmed.extend(allwords_stemmed) 
    
    allwords_tokenized = tokenize_only(i)
    totalvocab_tokenized.extend(allwords_tokenized)

In [ ]:
vocab_frame = pd.DataFrame({'words': totalvocab_tokenized}, index = totalvocab_stemmed)
print ('Всего ' + str(vocab_frame.shape[0]) + ' слов в словаре')

In [ ]:
tfidf_vectorizer = TfidfVectorizer( max_features=20000, min_df=1,  max_df=0.99,                                 
                                 use_idf=True, tokenizer=tokenize_and_stem, ngram_range=(1,5))


tfidf_matrix = tfidf_vectorizer.fit_transform(mob_comment.name)

print(tfidf_matrix.shape)
print(mob_comment['count'].shape)

In [ ]:
#mob_comment = mob_comment.reset_index()

In [ ]:
from sklearn.metrics.pairwise import cosine_similarity
dist = 1 - cosine_similarity(tfidf_matrix)

In [ ]:
# тут не работает
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

range_n_clusters = [ 2 , 5, 7 , 10, 15]            # clusters range you want to select
best_clusters = 0                       
previous_silh_avg = 0.0

for n_clusters in range_n_clusters:
    clusterer = KMeans(n_clusters=n_clusters)
    cluster_labels = clusterer.fit_predict(tfidf_matrix)
    silhouette_avg = silhouette_score(tfidf_matrix, cluster_labels)
    print (silhouette_avg)
    if silhouette_avg > previous_silh_avg:
        previous_silh_avg = silhouette_avg
        best_clusters = n_clusters
print (best_clusters)
# Final Kmeans for best_clusters

Кластеризация K-means


In [ ]:
from sklearn.cluster import KMeans
num_clusters = 5
km = KMeans(n_clusters=num_clusters)
%time km.fit(tfidf_matrix)

clusters = km.labels_.tolist()

In [ ]:
from sklearn.externals import joblib

joblib.dump(km,  'doc_cluster.pkl')

#km = joblib.load('doc_cluster.pkl')
clusters = km.labels_.tolist()

In [ ]:
comments = {'np': list(mob_comment['count']), 'comment': list(mob_comment.name), 'cluster': clusters}

#frame = pd.DataFrame(films, index = [clusters] , columns = ['rank', 'cluster'])
frame = pd.DataFrame(comments)

In [ ]:
frame['cluster'].value_counts()

In [ ]:
grouped = frame['np'].groupby(frame['cluster']) #groupby cluster for aggregation purposes

grouped.mean()

In [ ]:
terms = tfidf_vectorizer.get_feature_names()

In [ ]:
#отсортирует комментарии по близости к кластеру
order_centroids = km.cluster_centers_.argsort()[:, ::-1] 

for i in range(num_clusters):
    print("Кластер %d:" % i, end='')
    cust_word = list()
    for ind in order_centroids[i, :8]: #replace 6 with n words per cluster
        cust_word.append(vocab_frame.loc[terms[ind].split(' ')].values.tolist()[0][0])
    
    print(set(cust_word))    
    print()

Кластеризация дендограммы


In [ ]:
from scipy.cluster.hierarchy import ward, dendrogram

linkage_matrix = ward(dist) #define the linkage_matrix using ward clustering pre-computed distances

fig, ax = plt.subplots(figsize=(30, 150)) # set size
ax = dendrogram(linkage_matrix, orientation="right", labels=list(frame.comment));

plt.tick_params(\
    axis= 'x',          # changes apply to the x-axis
    which='both',      # both major and minor ticks are affected
    bottom='off',      # ticks along the bottom edge are off
    top='off',         # ticks along the top edge are off
    labelbottom='off')

plt.tight_layout() #show plot with tight layout

#uncomment below to save figure
plt.savefig('ward_clusters.png', dpi=200) #save figure as ward_clusters

LDA

LDA вероятностная модель, которая предполагает что документы это смесь топиков, и каждое слово принадлежит топику документа

In [ ]:
import string
def strip_proppers(text):
    # first tokenize by sentence, then by word to ensure that punctuation is caught as it's own token
    tokens = [word for sent in nltk.sent_tokenize(text) for word in nltk.word_tokenize(sent) if word.islower()]
    return "".join([" "+i if not i.startswith("'") and i not in string.punctuation else i for i in tokens]).strip()

In [ ]:
def tokenize_and_stem(text):
    # сначала токенизируем по предложению, потом по словам, чтобы сохранить "особые" слова
    tokens = [word for sent in nltk.sent_tokenize(text) for word in nltk.word_tokenize(sent)]
    filtered_tokens = []
    # удалим все самое плохое, знаки препинания
    for token in tokens:
        if re.search('[a-zA-Z0-9А-Яа-я]', token):
            filtered_tokens.append(token)
    stems = [stemmer.stem(t) for t in filtered_tokens]
    return stems

def tokenize_only(text):
    tokens = [word.lower() for sent in nltk.sent_tokenize(text) for word in nltk.word_tokenize(sent)]
    filtered_tokens = []
    for token in tokens:
        if re.search('[a-zA-Z0-9А-Яа-я]', token):
            filtered_tokens.append(token)
    return filtered_tokens

In [ ]:
from gensim import corpora, models, similarities 

%time preprocess = [strip_proppers(doc) for doc in mob_comment.mobile_app_comment]

#tokenize
%time tokenized_text = [tokenize_and_stem(text) for text in preprocess]

#remove stop words
%time texts = tokenized_text #[[word for word in text if word not in stopwords] for text in tokenized_text]

In [ ]:
from gensim.corpora import Dictionary

# to create the bigrams
bigram_model = Phrases(texts)
import codecs

if 1 == 1:

    with codecs.open('bigram_sentences_all.txt', 'w', encoding='utf_8') as f:        
        for unigram_sentence in texts:            
            bigram_sentence = u' '.join(bigram_model[unigram_sentence])            
            f.write(bigram_sentence + '\n')
            
from gensim.models.word2vec import LineSentence
bigram_sentences = LineSentence('bigram_sentences_all.txt')

trigram_model = Phrases(bigram_sentences)
triram_sentences = []
for bigram_sentence in bigram_sentences:
    triram_sentence = u' '.join(trigram_model[bigram_sentence])
    triram_sentences.append(triram_sentence)
#dictionary = Dictionary(texts)

In [ ]:
triram_sentences

In [ ]:
tokenized_text = [tokenize_only(text) for text in triram_sentences]

In [ ]:
dictionary = Dictionary(tokenized_text)

dictionary.filter_extremes(no_below=2, no_above=0.95)
corpus = [dictionary.doc2bow(doc) for doc in tokenized_text]
print('Number of unique tokens: %d' % len(dictionary))
print('Number of documents: %d' % len(corpus))

In [ ]:
num_topics_now = 6

In [ ]:
%time lda = models.LdaModel(corpus, num_topics = num_topics_now, id2word=dictionary,\
                            random_state = 1024, \
                            update_every=5, \
                            chunksize=10000, \
                            passes=100)\

In [ ]:
lda.show_topics()

In [ ]:
topics_matrix = lda.show_topics(formatted=False, num_words=20)

#topic_words = topics_matrix[:,:,1]
topic_num = 0
for i in range(0,num_topics):
    print('Топик №', i, end = ': ')
    for k in range(0,10):
        print(topics_matrix[i][1][k][0], end=', ')
    print()
    topic_num +=1

In [ ]:
lda.save('lda_simple_aproach.bin')

In [ ]:
temp = df_ans[['np_answer', 'mobile_app_comment']][df_ans.mobile_app_comment.isnull() == False]
list(temp.iloc[[14]].mobile_app_comment)

In [ ]:
doc = dictionary.doc2bow(texts[14])  
doc_lda = lda.get_document_topics(doc)
doc_lda

In [ ]:
top_topics = lda.top_topics(corpus, topn=20)

# Average topic coherence is the sum of topic coherences of all topics, divided by the number of topics.
avg_topic_coherence = sum([t[1] for t in top_topics]) / num_topics
print('Average topic coherence: %.4f.' % avg_topic_coherence)

In [ ]:
import pyLDAvis.gensim
pyLDAvis.enable_notebook()

pyLDAvis.gensim.prepare(lda, corpus, dictionary)
https://github.com/skipgram/modern-nlp-in-python/blob/master/executable/Modern_NLP_in_Python.ipynb биграммы https://radimrehurek.com/gensim/models/phrases.html https://markroxor.github.io/gensim/static/notebooks/lda_training_tips.html

In [ ]:
trigtam_sen = pd.concat([trigram_sen, df], axis=1)
trigtam_sen = trigtam_sen[trigram_sen.sentences.str.strip().str.len() != 0]

In [ ]:
text = 'Доказа́тельство с нулевы́м разглаше́нием (информа́ции) в криптографии (англ. Zero-knowledge proof) —  \
интерактивный криптографический протокол, позволяющий одной из взаимодействующих сторон («The verifier» — проверяющей) убедиться \
в достоверности какого-либо утверждения (обычно математического), не имея при этом никакой другой информации от второй стороны \
(«The prover» — доказывающей). Причём последнее условие является необходимым, так как обычно доказать, \
что сторона обладает определёнными сведениями в большинстве случаев тривиально, если она имеет право просто \
раскрыть информацию. Вся сложность состоит в том, чтобы доказать, что у одной из сторон есть информация, \
не раскрывая её содержание. Протокол должен учитывать, что доказывающий сможет убедить проверяющего только \
в случае, если утверждение действительно доказано. В противном случае сделать это будет невозможно,\
или крайне маловероятно из-за вычислительной сложности.\
Под интерактивностью протокола подразумевается непосредственный обмен информацией сторонами[1][2]. \
Таким образом, рассматриваемый протокол требует наличия интерактивных исходных данных (interactive input) \
от проверяющего, как правило, в виде задачи или проблемы. Цель легального доказывающего (имеющего доказательство) \
в этом протоколе — убедить проверяющего в том, что у него есть решение, не выдав при этом даже части «секретного» \
доказательства («нулевое разглашение»). Цель проверяющего же — это удостовериться в том, что доказывающая сторона \
«не лжёт»[2][3].\
Также были разработаны протоколы доказательства с нулевым разглашением[4][5], для которых не требовалось \
наличия интерактивных исходных данных, при этом доказательство которых, как правило, опирается на предположение об \
идеальной криптографической хеш-функции, то есть предполагается, что выход однонаправленной хеш-функции невозможно \
предсказать, если не известен её вход[6]'

In [ ]:
summarize(text, split= True)

In [ ]:
keywords(text, ratio=0.05, split=True)

In [ ]:
%%time
from gensim.corpora import Dictionary
from gensim.models import Phrases
# to create the bigrams
bigram_model = Phrases(texts)
import codecs

if 1 == 1:

    with codecs.open('bigram_sentences_all.txt', 'w', encoding='utf_8') as f:        
        for unigram_sentence in texts:            
            bigram_sentence = u' '.join(bigram_model[unigram_sentence])            
            f.write(bigram_sentence + '\n')
            
from gensim.models.word2vec import LineSentence
#bigram_sentences = LineSentence('bigram_sentences_all.txt')
#bigram_sentences = open('bigram_sentences_all.txt', "r").read().splitlines()


import codecs
fileObj = codecs.open( 'bigram_sentences_all.txt', "r", "utf_8_sig" )
bigram_sentences = fileObj.read().splitlines() # или читайте по строке
fileObj.close()

trigram_model = Phrases(bigram_sentences)
triram_sentences = []
i =0
for bigram_sentence in bigram_sentences:
    i += 1
    triram_sentence = u' '.join(trigram_model[tokenize_only(bigram_sentence)])
    triram_sentences.append(triram_sentence)
    
    #if len(triram_sentences) != i:
        #triram_sentence = u' '.join(' ')
print (i, len(triram_sentences))